/***************************************************************************
 *            MkheightmapWxDialog.h
 *
 *  Copyright  2008  Sebastian Mach
 *  phresnel@gmail.com
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 3 of the License, or (at your
 *  option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
 
 
#ifndef __MkheightmapWxDialog__
#define __MkheightmapWxDialog__

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>

#include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>

#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{ 
    using ::remove;
}
#endif

#include <boost/archive/tmpdir.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/xml_archive_exception.hpp>
#include <boost/archive/basic_xml_archive.hpp>


#define wxUSE_ABOUTDLG 1
//#include <wx/aboutdlg.h>
//#include <wx/generic/aboutdlgg.h>

#include <wx/stdpaths.h>
#include <wx/file.h>

#include <picogen/picogen.h>
#include <picogen/misc/picossdf.h>

#include <SciLexer.h>


#include "../../quantum/include/picogen/errors.h"



/**
@file
Subclass of MkheightmapWxDialogGui, which is generated by wxFormBuilder.
*/

#include "auto/mkheightmap_wx_gui.h"

/** Implementing MkheightmapWxDialogGui */
class MkheightmapWxDialog : public MkheightmapWxDialogGui
{
private:
    
    // TODO: Pimpl the following template functions.
    template <typename RT, typename T> RT floor (const T &v) const {
        assert (static_cast<int>(1.75) == 1);
        assert (static_cast<int>(1.5) == 1);
        assert (static_cast<int>(1.25) == 1);
        assert (static_cast<int>(-0.75) == 0);
        assert (static_cast<int>(-0.5) == 0);
        assert (static_cast<int>(-0.25) == 0);
        return static_cast <RT> (static_cast <int> (v<0 ? v-1 : v));
    }
    
    template <typename RT, typename T> RT round (const T &v) const {
        assert (static_cast<int>(1.75) == 1);
        assert (static_cast<int>(1.5) == 1);
        assert (static_cast<int>(1.25) == 1);
        assert (static_cast<int>(-0.75) == 0);
        assert (static_cast<int>(-0.5) == 0);
        assert (static_cast<int>(-0.25) == 0);
        return static_cast <RT> (static_cast <int> (floor <RT,T>(v+0.5)));
    }

    void UpdateTextWithTemplate (const wxString &tpl);   

    template <typename T> void GetYprOrientation (T &yaw, T &pitch, T &roll) const {        
        T deg_yaw   = +static_cast<T>(ypr_yaw->GetValue ()) + 0.01f * static_cast<float>(ypr_yaw_fine->GetValue ());
        T deg_pitch = -static_cast<T>(ypr_pitch->GetValue ())  + 0.01f * static_cast<float>(ypr_pitch_fine->GetValue ());
        T deg_roll  = -static_cast<T>(ypr_roll->GetValue ()) + 0.01f * static_cast<float>(ypr_roll_fine->GetValue ());
        
        const T to_radian = 0.0174532925;
        yaw   = deg_yaw * to_radian;
        pitch = deg_pitch * to_radian;
        roll  = deg_roll * to_radian;
    }    
    
    template <typename T> void SetYprOrientation (T yaw, T pitch, T roll) const {        
        const T to_degree = 1.0 / 0.0174532925;
        yaw *= to_degree;
        pitch *= -to_degree;
        roll *= -to_degree;
        
        // IMPORTANT TODO: ADD/SUB 0.5 BELOW, OTHERWISE THOSE SETTINGS WILL DEGENERATE ON EVERY LOAD
        const int master_yaw = floor <int> (0.5+yaw);
        const int master_pitch = floor <int> (0.5+pitch);
        const int master_roll = floor <int> (0.5+roll);
        
        ypr_yaw  ->SetValue (master_yaw);
        ypr_pitch->SetValue (master_pitch);
        ypr_roll ->SetValue (master_roll);
        
        ypr_yaw_fine  ->SetValue (floor <int> (0.5 + 100.0 * (yaw - static_cast <T> (master_yaw))));
        ypr_pitch_fine->SetValue (floor <int> (0.5 + 100.0 * (static_cast <T> (master_pitch) - pitch)));
        ypr_roll_fine ->SetValue (floor <int> (0.5 + 100.0 * (static_cast <T> (master_roll)  - roll)));
    }

    template <typename T> void GetYprPosition (T &x, T &y, T &z) const {
        using namespace std;
        {
            stringstream ss;
            ss << ypr_x->GetValue().mb_str() << flush;
            ss >> x;
        }        
        {
            stringstream ss;
            ss << ypr_y->GetValue().mb_str() << flush;
            ss >> y;
        }        
        {
            stringstream ss;
            ss << ypr_z->GetValue().mb_str() << flush;
            ss >> z;    
        }
    }
    
    template <typename T> void SetYprPosition (T x, T y, T z) const {
        using namespace std;
        {
            std::stringstream ss;
            ss << x << std::flush;
            ypr_x->SetValue (wxString (ss.str().c_str(),wxConvUTF8));
        }
        {
            std::stringstream ss;
            ss << y << std::flush;
            ypr_y->SetValue (wxString (ss.str().c_str(),wxConvUTF8));
        }
        {
            std::stringstream ss;
            ss << z << std::flush;
            ypr_z->SetValue (wxString (ss.str().c_str(),wxConvUTF8));
        }
    }
    
    
    
    // TODO: Refactor into two seperate functions for sun and atmosphere.
    template <typename T> void ObtainSunSkyParams (
        T atmoRGB [3], T &atmosphereBrightness, 
        bool &fogEnable, T &fogDensity, T &fogMaxRange,
        T &turbidity,
        T &sunDiskSize,
        bool &falloffEnable, T falloffParameters [3],
        T sunRGB [3], T &sunBrightness,
        T sunDir [3]
    ) const {
        // ATMOSPHERE
        
        // Atmosphere Color Filter.
        atmosphereBrightness = static_cast <T> (atmosphereIntensity->GetValue ()) * 0.001;
        atmoRGB [0] = static_cast <T> (atmosphereR->GetValue ()) * 0.01;
        atmoRGB [1] = static_cast <T> (atmosphereG->GetValue ()) * 0.01;
        atmoRGB [2] = static_cast <T> (atmosphereB->GetValue ()) * 0.01;
        
        // Fog.
        fogEnable = this->fogEnable->IsChecked ();
        fogDensity  = static_cast <T> (this->fogDensity->GetValue ()) * 0.0000001; // / 10k
        fogMaxRange = static_cast <T> (this->fogMaxRange->GetValue ());
        
        // Turbidity.
        turbidity = static_cast <T> (turbidityA->GetValue ()) + 0.01 * static_cast <T> (turbidityB->GetValue ());
        
        // SUN    
        // Disk Size.
        sunDiskSize = static_cast <T> (this->diskSize->GetValue ()) * 0.01;
        
        // Falloff.
        falloffParameters [0]   = static_cast <T> (this->falloffParameterA->GetValue ()) * 0.1;
        falloffParameters [1]   = static_cast <T> (this->falloffParameterB->GetValue ()) * 0.00001;
        falloffParameters [2]   = static_cast <T> (this->falloffParameterC->GetValue ()) * 0.1;
        falloffEnable = this->falloffEnable->IsChecked ();
        
        // Sun Color.
        sunBrightness = static_cast <T> (this->sunIntensity->GetValue ());
        sunRGB [0] = static_cast <T> (sunR->GetValue ()) * 0.01;
        sunRGB [1] = static_cast <T> (sunG->GetValue ()) * 0.01;
        sunRGB [2] = static_cast <T> (sunB->GetValue ()) * 0.01;
        
        // Sun Direction (mkskymap will normalise it for us).
        const T to_radian = 0.0174532925f;
        const T pi = 3.14159265;
        const T sunPhi   = to_radian * static_cast <T> (this->sunPhi->GetValue()) + 0.5*pi;
        const T sunTheta = to_radian * static_cast <T> (this->sunTheta->GetValue());
        sunDir [0] = sin (sunTheta) * cos (sunPhi);
        sunDir [1] = cos (sunTheta);
        sunDir [2] = sin (sunTheta) * sin (sunPhi);
    }
    
    
    // TODO: Refactor into two seperate functions for sun and atmosphere.
    template <typename T> void SetSunSkyParams (
        const T atmoRGB [3], const  T atmosphereBrightness,
        const bool fogEnable, const T fogDensity, const T fogMaxRange,
        const T turbidity,
        const T sunDiskSize,
        const bool falloffEnable, const T falloffParameters [3],
        const T sunRGB [3], const T sunBrightness,
        const T sunDir [3]
    ) const {
        // ATMOSPHERE
        
        // Atmosphere Color Filter.        
        atmosphereIntensity->SetValue (static_cast <int> (0.5 + atmosphereBrightness / 0.001));
        atmosphereR->SetValue (static_cast <int> (0.5 + atmoRGB [0] / 0.01));
        atmosphereG->SetValue (static_cast <int> (0.5 + atmoRGB [1] / 0.01));
        atmosphereB->SetValue (static_cast <int> (0.5 + atmoRGB [2] / 0.01));
        
        // Fog.
        this->fogEnable->SetValue (fogEnable);
        this->fogDensity->SetValue (static_cast <int> (0.5 + fogDensity / 0.0000001));
        this->fogMaxRange->SetValue (static_cast <int> (0.5 + fogMaxRange));
        
        
        // Turbidity.
        this->turbidityA->SetValue (floor <int> (turbidity));
        this->turbidityB->SetValue (static_cast <int> (0.5 + (turbidity - floor <T> (turbidity)) / 0.01));

        // SUN    
        // Disk Size.
        this->diskSize->SetValue (static_cast <int> (0.5 + sunDiskSize / 0.01));
        
        // Falloff.
        this->falloffEnable->SetValue (falloffEnable);
        this->falloffParameterA->SetValue (static_cast <int> (0.5 + falloffParameters [0] / 0.1));
        this->falloffParameterB->SetValue (static_cast <int> (0.5 + falloffParameters [1] / 0.00001));
        this->falloffParameterC->SetValue (static_cast <int> (0.5 + falloffParameters [2] / 0.1));
        
        // Sun Color.
        sunIntensity->SetValue (static_cast <int> (0.5 + sunBrightness));
        sunR->SetValue (static_cast <int> (0.5 + sunRGB [0] / 0.01));
        sunG->SetValue (static_cast <int> (0.5 + sunRGB [1] / 0.01));
        sunB->SetValue (static_cast <int> (0.5 + sunRGB [2] / 0.01));
        
        // Sun Direction (mkskymap will normalise it for us).
        /*
        const T to_radian = 0.0174532925f;
        const T pi = 3.14159265;
        const T sunPhi   = to_radian * static_cast <T> (this->sunPhi->GetValue()) + 0.5*pi;
        const T sunTheta = to_radian * static_cast <T> (this->sunTheta->GetValue());
        sunDir [0] = sin (sunTheta) * cos (sunPhi);
        sunDir [1] = cos (sunTheta);
        sunDir [2] = sin (sunTheta) * sin (sunPhi);
        */
        const T pi = 3.14159265;
        const T to_degree = 1.0 / 0.0174532925f;
        const T r2 = sunDir [0] * sunDir [0] + sunDir [1] * sunDir [1] + sunDir [2] * sunDir [2];
        const T r = sqrt (r2);
        const T ir = 1.0 / r;
        const T n [3] = {sunDir [0] * ir, sunDir [1] * ir, sunDir [2] * ir};
        const T phi = atan2 (n [2], n [0]);
        const T theta = acos (n [1] / r);
        
        const T phi2 = (phi - 0.5*pi);
        const int phi3 = static_cast <int> (to_degree * phi2 + (phi2<0.0?-0.5:0.5));
        const int phi4 = phi3 < -180 ? phi3 + 360 : phi3;
        
        this->sunPhi->SetValue (phi4);
        this->sunTheta->SetValue (static_cast <int> (0.5 + to_degree * theta));
        using namespace std;
        if (0) {
            cout << "r=" << r << std::endl;
            cout << "r2=" << r2 << std::endl;
            cout << "ir=" << ir << std::endl;        
            cout << "sunDir={" << sunDir [0] << ", " << sunDir [1] << ", " << sunDir [2] << "}" << std::endl;
            cout << "sunDir'={" << n [0] << ", " << n [1] << ", " << n [2] << "}" << std::endl;        
        }
    }
    

    bool ShowSaveFileDlg();

    std::string generateSceneTempFile (bool withPreviewSettings) const;    
    wxString pwsFilename;
    
    void run (wxString program, wxString x_usrbin);
    void showRunError (const picogen::error_codes::code_t code, const wxArrayString &output, const wxArrayString &errors);

protected:
	// Handlers for MkheightmapWxDialogGui events.
	void OnSave ( wxCommandEvent& event );
	void OnLoad ( wxCommandEvent& event );
	void OnShowHeightmap( wxCommandEvent& event );
	void OnShowShadedHeightmap( wxCommandEvent& event );
	void OnClose( wxCommandEvent& event );
	void OnRender ( wxCommandEvent& event );
	void OnAutoformat( wxCommandEvent& event );
	void OnOpenSaveFile( wxCommandEvent& event );
	void OnQuickPreview( wxCommandEvent& event );
	void OnShowHemisphere( wxCommandEvent& event );
    void OnFastHsMakeChoice ( wxCommandEvent& event );
    void OnFastColorMakeChoice ( wxCommandEvent& event );
	void OnFast1MakeChoice ( wxCommandEvent& event );
	void OnFast11MakeChoice ( wxCommandEvent& event );
	void OnFast2MakeChoice ( wxCommandEvent& event );
	void OnFast21MakeChoice ( wxCommandEvent& event );
	void OnFast3MakeChoice ( wxCommandEvent& event );
	void OnFast31MakeChoice ( wxCommandEvent& event );
	void OnFast4MakeChoice ( wxCommandEvent& event );
	void OnFast41MakeChoice ( wxCommandEvent& event );
	void OnFast5MakeChoice ( wxCommandEvent& event );
	void OnFast51MakeChoice ( wxCommandEvent& event );
	void OnFast6MakeChoice ( wxCommandEvent& event );
	void OnPresets1 ( wxCommandEvent& event );
    void OnMenu_Execute (wxCommandEvent& event);
    void OnMenu_Copyright (wxCommandEvent& event);
	
public:
	/** Constructor */
	MkheightmapWxDialog( wxWindow* parent );
};


#endif // __MkheightmapWxDialog__
